package com.yh.utils;

import cn.hutool.core.util.ObjectUtil;
import com.yh.config.OtherSource;
import com.yh.entity.Actor;
import com.yh.entity.Director;
import com.yh.entity.Movie;
import com.yh.entity.User;
import io.minio.GetPresignedObjectUrlArgs;
import io.minio.MinioClient;
import io.minio.StatObjectArgs;
import io.minio.StatObjectResponse;
import io.minio.http.Method;
import org.apache.log4j.Logger;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Supplier;

/**
 * 把图片的基本地址替换成图片在远程服务器的访问地址；处理任意实体对象的图片地址，对象类型由使用者决定。
 * 通过快捷方法获取远程访问地址，将优先考虑获取图片在MinIO服务器中的访问地址；如果获取失败，将会获取图
 * 片在Nginx服务器中的访问地址
 *
 * @author yuhuan
 * @client MinIO服务器客户端
 * @otherSource 读取外部信息的配置信息类
 * @supplier 提供者函数。用于提供图片的基本地址，通过get方法获取对象的地址信息
 * @consumer 消费者函数。用于封装图片的真实访问地址，通过set方法给对象赋值
 * @<T> t       需要处理图片访问地址的对象
 * @date 2022/01/13
 */
public class ImgUrl {
    /**
     * 日志
     */
    private static final Logger logger = Logger.getLogger(ImgUrl.class);

    /**
     * 获取图片在Nginx服务器中的访问地址
     */
    public static <T> T imgUrlHandlerInNginx(
            OtherSource source,
            Supplier<String> supplier,
            Consumer<String> consumer, T t) {
        // 获取基本地址信息（如：/user/xxx.jpg）
        String basicImgUrl = supplier.get();
        // 基本地址不存在
        if (ObjectUtil.isEmpty(basicImgUrl)) {
            logger.warn("<<<<< 基础地址为空，终止本次处理");
            return t;
        }
        // 远程地址放行
        if (isRemoteAddress(basicImgUrl, source)) {
            logger.warn("<<<<< 检查到" + basicImgUrl + "已经是远程地址，终止本次处理");
            return t;
        }
        // 用于保存完整的图片访问地址
        String integratedImgUrl = basicImgUrl;
        // 拼接图片在Nginx服务器的访问地址
        integratedImgUrl = source.nginxUrl + integratedImgUrl;
        // 将对象中的基本地址"/user/xxx.jpg"替换成"http://192.168.74.129:80/images/user/xxx.jpg"
        consumer.accept(integratedImgUrl);
        return t;
    }

    /**
     * 获取图片在MinIO服务器中的访问地址
     */
    public static <T> T imgUrlHandlerInMinIo(
            MinioClient client,
            OtherSource source,
            Supplier<String> supplier,
            Consumer<String> consumer, T t) throws Throwable {
        // 获取基本地址信息（如：/user/xxx.jpg）
        String basicImgUrl = supplier.get();
        // 基本地址不存在
        if (ObjectUtil.isEmpty(basicImgUrl)) {
            logger.warn("<<<<< 基础地址为空，终止本次处理");
            return t;
        }
        // 远程地址放行
        if (isRemoteAddress(basicImgUrl, source)) {
            logger.warn("<<<<< 检查到" + basicImgUrl + "已经是远程地址，终止本次处理");
            return t;
        }
        // 截取图片储存桶的名称
        String bucketName = basicImgUrl.split("/")[1];
        // 获取图片的文件名
        String imgName = basicImgUrl.split("/")[2];
        // 获取文件在minio服务器中的访问URL
        String integratedImgUrl = client.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                .bucket(bucketName)
                .object(imgName)
                .method(Method.GET)
                .build());
        consumer.accept(integratedImgUrl);
        return t;
    }

    /**
     * 检查文件的地址是不是远程地址
     */
    private static boolean isRemoteAddress(String basicImgUrl, OtherSource source) {
        // minio 的服务器地址
        if (basicImgUrl.contains(source.minioUrl)) {
            return true;
        }
        // nginx 的服务器地址
        if (basicImgUrl.contains(source.nginxUrl)) {
            return true;
        }
        return false;
    }

    /**
     * 检查图片或文件是否存在于MinIO中。如果出现任何异常，如：404;都将直接返回文件不存在
     */
    public static boolean isExistFromMinIo(
            MinioClient client,
            String bucketName,
            String fileName) {
        // 获取图片的文件名
        String imgName = fileName.split("/")[2];
        StatObjectResponse objectResponse = null;
        try {
            objectResponse = client.statObject(StatObjectArgs.builder()
                    .bucket(bucketName)
                    .object(imgName)
                    .build());
        } catch (Exception e) {
            return false;
        }
        if (objectResponse != null && objectResponse.size() > 0) {
            return true;
        }
        return false;
    }

    /**
     * 拼接保存到数据的基本URL（储存桶的名称+图片的名称）
     *
     * @bucketName 图片保存在minio服务器的储存桶的名称 如：user
     * @fileName 图片的原始文件名 如：xxx.jpg
     */
    public static String spliceBasicUrl(String bucketName, String fileName) {
        String basicUrl = null;
        if (ObjectUtil.isEmpty(fileName)) {
            return basicUrl;
        }
        basicUrl = "/" + fileName;
        if (ObjectUtil.isEmpty(bucketName)) {
            return basicUrl;
        }
        basicUrl = "/" + bucketName + basicUrl;
        return basicUrl;
    }

    /**
     * 拼接图片在nginx中的文件夹地址
     *
     * @nginxPath nginx的文件服务地址
     * @bucketName 文件保存的文件夹名称（同mimio桶名）
     */
    public static String spliceNginxBasicFileUrl(String nginxPath, String bucketName) {
        String basicFileUrl = nginxPath;
        if (ObjectUtil.isEmpty(bucketName)) {
            return basicFileUrl;
        }
        basicFileUrl = basicFileUrl + "/" + bucketName;
        return basicFileUrl;
    }

    /**
     * 快捷方法 处理用户头像地址
     */
    public static User userImageHandler(MinioClient client, OtherSource source, User user) {
        if (ObjectUtil.isEmpty(user)) {
            return user;
        }
        // 编辑取出头像基本地址的函数
        Supplier<String> supplier = () -> user.getUserHeadImg();
        // 编辑替换头像基本地址的函数
        Consumer<String> consumer = (u) -> user.setUserHeadImg(u);
        User idealUser = user;
        try {
            boolean existFromMinIo = isExistFromMinIo(client, source.userFileUrl, user.getUserHeadImg());
            if (existFromMinIo) {
                // 取出头像在minio服务器中的访问地址
                idealUser = imgUrlHandlerInMinIo(client, source, supplier, consumer, user);
            } else {
                idealUser = imgUrlHandlerInNginx(source, supplier, consumer, user);
            }
        } catch (Throwable e) {
            logger.error("获取" + user.getUserHeadImg() + "在minio服务器的访问地址失败！将尝试获取在Nginx服务器的访问地址");
            idealUser = imgUrlHandlerInNginx(source, supplier, consumer, user);
        }
        return idealUser;
    }

    /**
     * 快捷方法 处理演员的写真地址
     */
    public static Actor actorImageHandler(MinioClient client, OtherSource source, Actor actor) {
        if (ObjectUtil.isEmpty(actor)) {
            return actor;
        }
        // 编辑取出写真基本地址的函数
        Supplier<String> supplier = () -> actor.getActorPicture();
        // 编辑替换写真基本地址的函数
        Consumer<String> consumer = (a) -> actor.setActorPicture(a);
        Actor idealActor = actor;
        try {
            boolean existFromMinIo = isExistFromMinIo(client, source.actorFileUrl, actor.getActorPicture());
            if (existFromMinIo) {
                // 取出写真在minio服务器中的访问地址
                idealActor = imgUrlHandlerInMinIo(client, source, supplier, consumer, actor);
            } else {
                idealActor = imgUrlHandlerInNginx(source, supplier, consumer, actor);
            }
        } catch (Throwable throwable) {
            logger.error("获取" + actor.getActorPicture() + "在minio服务器的访问地址失败！将尝试获取在Nginx服务器的访问地址");
            idealActor = imgUrlHandlerInNginx(source, supplier, consumer, actor);
        }
        return idealActor;
    }

    /**
     * 快捷方法 处理导演的写真地址
     */
    public static Director directorImageHandler(MinioClient client, OtherSource source, Director director) {
        if (ObjectUtil.isEmpty(director)) {
            return director;
        }
        // 编辑取出写真基本地址的函数
        Supplier<String> supplier = () -> director.getDirectorPicture();
        // 编辑替换写真基本地址的函数
        Consumer<String> consumer = (d) -> director.setDirectorPicture(d);
        Director idealDirector = director;
        try {
            boolean existFromMinIo = isExistFromMinIo(client, source.directorFileUrl, director.getDirectorPicture());
            if (existFromMinIo) {
                // 取出写真在minio服务器中的访问地址
                idealDirector = imgUrlHandlerInMinIo(client, source, supplier, consumer, director);
            } else {
                idealDirector = imgUrlHandlerInNginx(source, supplier, consumer, director);
            }
        } catch (Throwable throwable) {
            logger.error("获取" + director.getDirectorPicture() + "在minio服务器的访问地址失败！将尝试获取在Nginx服务器的访问地址");
            idealDirector = imgUrlHandlerInNginx(source, supplier, consumer, director);
        }
        return idealDirector;
    }

    /**
     * 快捷方法 处理影片的海报地址
     */
    public static Movie movieImageHandler(MinioClient client, OtherSource source, Movie movie) {
        if (ObjectUtil.isEmpty(movie)) {
            return movie;
        }
        // 编辑取出海报基本地址的函数
        Supplier<String> supplier = () -> movie.getMoviePicture();
        // 编辑替换海报基本地址的函数
        Consumer<String> consumer = (m) -> movie.setMoviePicture(m);
        Movie idealMovie = movie;
        try {
            boolean existFromMinIo = isExistFromMinIo(client, source.movieFileUrl, movie.getMoviePicture());
            if (existFromMinIo) {
                // 取出海报在minio服务器中的访问地址
                idealMovie = imgUrlHandlerInMinIo(client, source, supplier, consumer, movie);
            } else {
                idealMovie = imgUrlHandlerInNginx(source, supplier, consumer, movie);
            }
        } catch (Throwable throwable) {
            logger.error("获取" + movie.getMoviePicture() + "在minio服务器的访问地址失败！将尝试获取在Nginx服务器的访问地址");
            idealMovie = imgUrlHandlerInNginx(source, supplier, consumer, movie);
        }
        return idealMovie;
    }

    /**
     * 重载 多影片处理
     */
    public static List<Movie> movieImageHandler(MinioClient client, OtherSource source, List<Movie> movies) {
        List<Movie> movieList = new ArrayList<>();
        for (Movie movie : movies) {
            movie = movieImageHandler(client, source, movie);
            movieList.add(movie);
        }
        return movieList;
    }
}
